/**
 * @license
 * Copyright 2021 Du Tian Wei
 * SPDX-License-Identifier: Apache-2.0
 */

OpenBlock.Blocks = {};
OpenBlock.Blocks.getAvaliableVar = function (block, r) {
    if (!r) {
        r = [];
    }
    if (block.local_variable_info) {
        block.local_variable_info.forEach(i => {
            r.push(i);
        });
    }
    if (block.inputList) {
        block.inputList.forEach(input => {
            if (input.connection && input.connection.isConnected()) {
                OpenBlock.Blocks.getAvaliableVar(input.connection.targetConnection.getSourceBlock(), r);
            }
        })
    }
    if (block.nextConnection && block.nextConnection.isConnected()) {
        OpenBlock.Blocks.getAvaliableVar(block.nextConnection.targetConnection.getSourceBlock(), r);
    }
    return r;
}
function isChildBlockOf(b1, b2) {
    if (!(b1 instanceof Blockly.Block)) {
        return false;
    }
    if (!(b2 instanceof Blockly.Block)) {
        return false;
    }
    while (b1) {
        if (b1 === b2) {
            return true;
        }
        b1 = b1.getParent();
    }
    return false;
}
Blockly.defineBlocksWithJsonArray(
    [
        // Block for composing a colour from RGB components.
        {
            "type": "colour_rgb",
            "message0": "%{BKY_COLOUR_RGB_TITLE} %{BKY_COLOUR_RGB_RED} %1 %{BKY_COLOUR_RGB_GREEN} %2 %{BKY_COLOUR_RGB_BLUE} %3",
            "args0": [
                {
                    "type": "input_value",
                    "name": "RED",
                    "check": "Integer",
                    "align": "RIGHT"
                },
                {
                    "type": "input_value",
                    "name": "GREEN",
                    "check": "Integer",
                    "align": "RIGHT"
                },
                {
                    "type": "input_value",
                    "name": "BLUE",
                    "check": "Integer",
                    "align": "RIGHT"
                }
            ],
            "output": "Colour",
            "helpUrl": "%{BKY_COLOUR_RGB_HELPURL}",
            "style": "colour_blocks",
            "tooltip": "%{BKY_COLOUR_RGB_TOOLTIP}"
        },
        {
            "type": "text_isEmpty",
            "message0": "%{BKY_TEXT_ISEMPTY_TITLE}",
            "args0": [
                {
                    "type": "input_value",
                    "name": "VALUE",
                    "check": ['String']
                }
            ],
            "output": 'Boolean',
            "style": "text_blocks",
            "tooltip": "%{BKY_TEXT_ISEMPTY_TOOLTIP}",
            "helpUrl": "%{BKY_TEXT_ISEMPTY_HELPURL}"
        },
        {
            "type": "text_indexOf",
            "message0": "%{BKY_TEXT_INDEXOF_TITLE}",
            "args0": [
                {
                    "type": "input_value",
                    "name": "VALUE",
                    "check": "String"
                },
                {
                    "type": "field_dropdown",
                    "name": "END",
                    "options": [
                        [
                            "%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}",
                            "FIRST"
                        ],
                        [
                            "%{BKY_TEXT_INDEXOF_OPERATOR_LAST}",
                            "LAST"
                        ]
                    ]
                },
                {
                    "type": "input_value",
                    "name": "FIND",
                    "check": "String"
                }
            ],
            "output": "Integer",
            "style": "text_blocks",
            "helpUrl": "%{BKY_TEXT_INDEXOF_HELPURL}",
            "inputsInline": true,
            "extensions": [
                "text_indexOf_tooltip"
            ]
        },
        // Block for rounding functions.
        {
            "type": "math_round",
            "message0": "%1 %2",
            "args0": [
                {
                    "type": "field_dropdown",
                    "name": "OP",
                    "options": [
                        ["%{BKY_MATH_ROUND_OPERATOR_ROUND}", "ROUND"],
                        ["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}", "ROUNDUP"],
                        ["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}", "ROUNDDOWN"]
                    ]
                },
                {
                    "type": "input_value",
                    "name": "NUM",
                    "check": ["Number"]
                }
            ],
            "output": ["Integer"],
            "style": "math_blocks",
            "helpUrl": "%{BKY_MATH_ROUND_HELPURL}",
            "tooltip": "%{BKY_MATH_ROUND_TOOLTIP}"
        },
        {
            "type": "text_length",
            "message0": "%{BKY_TEXT_LENGTH_TITLE}",
            "args0": [
                {
                    "type": "input_value",
                    "name": "VALUE",
                    "check": ['String',]
                }
            ],
            "output": ['Integer', 'Number'],
            "style": "text_blocks",
            "tooltip": "%{BKY_TEXT_LENGTH_TOOLTIP}",
            "helpUrl": "%{BKY_TEXT_LENGTH_HELPURL}"
        }, {
            "type": "logic_is_not_valid",
            "message0": Blockly.Msg["logic_is_not_valid"],
            "args0": [{
                "type": "input_value",
                "name": "A0"
            }],
            "inputsInline": true,
            "output": "Boolean",
            "style": "operation_blocks",
            "tooltip": "",
            "helpUrl": ""
        },
        {
            "type": "logic_is_valid",
            "message0": Blockly.Msg["logic_is_valid"],
            "args0": [{
                "type": "input_value",
                "name": "A0"
            }],
            "inputsInline": true,
            "output": "Boolean",
            "style": "operation_blocks",
            "tooltip": "",
            "helpUrl": ""
        }, {
            "type": "variables_self",
            "message0": Blockly.Msg["variables_self"],
            "inputsInline": true,
            "output": "FSM",
            "style": "gameobject_blocks",
            "tooltip": "",
            "helpUrl": ""
        }, {
            "type": "text_comment",
            "message0": "注释 %1",
            "args0": [
                {
                    "type": "field_multilinetext",
                    "name": "TEXT",
                    "text": "注释"
                }
            ],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "text_blocks",
            "extensions": [
                "tooltip_when_inline"
            ]
        },
        {
            "type": "destroy_fsm",
            "message0": Blockly.Msg["destroy_fsm"],
            "previousStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        },
        // Block for checking if a number is even, odd, prime, whole, positive,
        // negative or if it is divisible by certain number.
        {
            "type": "math_number_property",
            "message0": "%1 %2",
            "args0": [
                {
                    "type": "input_value",
                    "name": "NUMBER_TO_CHECK",
                    "check": "Number"
                },
                {
                    "type": "field_dropdown",
                    "name": "PROPERTY",
                    "options": [
                        ["%{BKY_MATH_IS_EVEN}", "EVEN"],
                        ["%{BKY_MATH_IS_ODD}", "ODD"],
                        // ["%{BKY_MATH_IS_PRIME}", "PRIME"],
                        ["%{BKY_MATH_IS_WHOLE}", "WHOLE"],
                        ["%{BKY_MATH_IS_POSITIVE}", "POSITIVE"],
                        ["%{BKY_MATH_IS_NEGATIVE}", "NEGATIVE"],
                        ["%{BKY_MATH_IS_DIVISIBLE_BY}", "DIVISIBLE_BY"]
                    ]
                }
            ],
            "inputsInline": true,
            "output": "Boolean",
            "style": "math_blocks",
            "tooltip": "%{BKY_MATH_IS_TOOLTIP}",
            "mutator": "math_is_divisibleby_mutator"
        },
        {
            "type": "controls_repeat_ext",
            "message0": "%{BKY_CONTROLS_REPEAT_TITLE}",
            "args0": [{
                "type": "input_value",
                "name": "TIMES",
                "check": ["Integer", "Number"]
            }],
            "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
            "args1": [{
                "type": "input_statement",
                "name": "DO",
                "check": "inst"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "loop_blocks",
            "tooltip": "%{BKY_CONTROLS_REPEAT_TOOLTIP}",
            "helpUrl": "%{BKY_CONTROLS_REPEAT_HELPURL}"
        },
        // Block for random integer between [X] and [Y].
        {
            "type": "math_random_int",
            "message0": "%{BKY_MATH_RANDOM_INT_TITLE}",
            "args0": [
                {
                    "type": "input_value",
                    "name": "FROM",
                    "check": "Number"
                },
                {
                    "type": "input_value",
                    "name": "TO",
                    "check": ["Number", "Integer"]
                }
            ],
            "inputsInline": true,
            "output": ["Integer", "Number"],
            "style": "math_blocks",
            "tooltip": "%{BKY_MATH_RANDOM_INT_TOOLTIP}",
            "helpUrl": "%{BKY_MATH_RANDOM_INT_HELPURL}"
        }
    ]
);
Blockly.Blocks['text_count'] = {
    /**
     * Block for counting how many times one string appears within another string.
     * @this {Blockly.Block}
     */
    init: function () {
        this.jsonInit({
            "message0": Blockly.Msg['TEXT_COUNT_MESSAGE0'],
            "args0": [
                {
                    "type": "input_value",
                    "name": "SUB",
                    "check": "String"
                },
                {
                    "type": "input_value",
                    "name": "TEXT",
                    "check": "String"
                }
            ],
            "output": "Integer",
            "inputsInline": true,
            "style": "text_blocks",
            "tooltip": Blockly.Msg['TEXT_COUNT_TOOLTIP'],
            "helpUrl": Blockly.Msg['TEXT_COUNT_HELPURL']
        });
    }
};
// 修改支持的循环块类型
Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.push('controls_repeat_ext');
Blockly.Blocks["controls_for"] = {
    init() {
        this.jsonInit(
            {
                "type": "controls_for",
                "message0": "%{BKY_CONTROLS_FOR_TITLE}",
                "args0": [
                    {
                        "type": "field_input",
                        "name": "VAR"
                    },
                    {
                        "type": "input_value",
                        "name": "FROM",
                        "check": "Integer",
                        "align": "RIGHT"
                    },
                    {
                        "type": "input_value",
                        "name": "TO",
                        "check": "Integer",
                        "align": "RIGHT"
                    },
                    {
                        "type": "input_value",
                        "name": "BY",
                        "check": "Integer",
                        "align": "RIGHT"
                    }
                ],
                "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
                "args1": [{
                    "type": "input_statement",
                    "name": "DO",
                    "check": "inst"
                }],
                "inputsInline": true,
                "previousStatement": "inst",
                "nextStatement": "inst",
                "style": "loop_blocks",
                "helpUrl": "%{BKY_CONTROLS_FOR_HELPURL}",
                "extensions": [
                    // "contextMenu_newGetVariableBlock",
                    "controls_for_tooltip"
                ]
            });
    },
    onchange(e) {
        if (this.isInFlyout) {
            return;
        }
        let name = this.getFieldValue('VAR');
        let info = { name, type: "Integer", blockId: this.id };
        this.local_variable_info = [info];
    },
    mutationToDom() {
        let name = this.getFieldValue('VAR');
        let info = { name, type: "Integer", blockId: this.id };
        this.local_variable_info = [info];
        let xml = '<mutation>' + name + '</mutation>';
        let dom = Blockly.Xml.textToDom(xml);
        return dom;
    },
    domToMutation(xml) {
        let name = xml.innerHTML;
        let info = { name, type: "Integer", blockId: this.id };
        this.local_variable_info = [info];
    },
};
Blockly.Blocks["controls_whileUntil"] = {
    init() {
        this.jsonInit({
            "type": "controls_whileUntil",
            "message0": "%1 %2",
            "args0": [
                {
                    "type": "field_dropdown",
                    "name": "MODE",
                    "options": [
                        ["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}", "WHILE"],
                        ["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}", "UNTIL"]
                    ]
                },
                {
                    "type": "input_value",
                    "name": "BOOL",
                    "check": "Boolean"
                }
            ],
            "message1": "%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",
            "args1": [{
                "type": "input_statement",
                "name": "DO",
                "check": "inst"
            }],
            "previousStatement": 'inst',
            "nextStatement": 'inst',
            "style": "loop_blocks",
            "helpUrl": "%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",
            "extensions": ["controls_whileUntil_tooltip"]
        });
    }
}
Blockly.Blocks['ub_math_arithmetic'] = {
    init() {
        this.jsonInit(
            // Block for basic arithmetic operator.
            {
                "type": "ub_math_arithmetic",
                "message0": "%1 %2 %3",
                "args0": [
                    {
                        "type": "input_value",
                        "name": "A",
                        "check": ["Number", "Integer"]
                    },
                    {
                        "type": "field_dropdown",
                        "name": "OP",
                        "options": [
                            ["%{BKY_MATH_ADDITION_SYMBOL}", "ADD"],
                            ["%{BKY_MATH_SUBTRACTION_SYMBOL}", "MINUS"],
                            ["%{BKY_MATH_MULTIPLICATION_SYMBOL}", "MULTIPLY"],
                            ["%{BKY_MATH_DIVISION_SYMBOL}", "DIVIDE"],
                            ["%{BKY_MATH_POWER_SYMBOL}", "POWER"]
                        ]
                    },
                    {
                        "type": "input_value",
                        "name": "B",
                        "check": ["Number", "Integer"]
                    }
                ],
                "inputsInline": true,
                "output": ["Number", 'Integer'],
                "style": "math_blocks",
                "helpUrl": "%{BKY_MATH_ARITHMETIC_HELPURL}",
                "extensions": ["math_op_tooltip"]
            });
    },
    // mutationToDom() {
    //     let xml = '<mutation>' + encodeURI(JSON.stringify(this.outputConnection.check_)) + '</mutation>';
    //     let dom = Blockly.Xml.textToDom(xml);
    //     return dom;
    // },
    // domToMutation(xml) {
    //     if (xml && xml.textContent) {
    //         try {
    //             let info = JSON.parse(decodeURI(xml.textContent));
    //             this.setOutput(true, info);
    //         } catch (e) { }
    //     }
    // },
    // onchange(e) {
    //     if (e.recordUndo && isChildBlockOf(this.workspace.getBlockById(e.blockId), this) || (e instanceof Blockly.Events.Create)) {
    //         function isInt(input) {
    //             if (input.connection.isConnected()) {
    //                 let check = input.connection.targetConnection.check_;
    //                 if (!check) {
    //                     return true;
    //                 }
    //                 if (check.indexOf('Integer') > -1) {
    //                     return true;
    //                 } else {
    //                     return false;
    //                 }
    //             } else {
    //                 return true;
    //             }
    //         }
    //         if (isInt(this.getInput('A')) && isInt(this.getInput('B'))) {
    //             this.setOutput(true, 'Integer');
    //         } else {
    //             this.setOutput(true, ['Number','Integer'])
    //         }
    //     }
    // }
}
Blockly.Blocks['text_print'] = {
    /**
     * Block for print statement.
     * @this {Blockly.Block}
     */
    init: function () {
        this.jsonInit({
            "message0": Blockly.Msg['TEXT_PRINT_TITLE'],
            "args0": [
                {
                    "type": "input_value",
                    "name": "TEXT"
                }
            ],
            "style": "text_blocks",
            "tooltip": Blockly.Msg['TEXT_PRINT_TOOLTIP'],
            "helpUrl": Blockly.Msg['TEXT_PRINT_HELPURL'],
            "nextStatement": ["inst"],
            "previousStatement": ["inst"]
        });
    },
};

Blockly.Blocks["math_integer"] = {
    init: function () {
        this.jsonInit({
            "message0": "整数 %1",
            "args0": [{
                "type": "field_input",
                "name": "NUM",
            }
            ],
            "style": "struct_blocks",
            "output": ["Integer", "Number"]
        });
    },
    onchange(e) {
        if (e.blockId === this.id) {
            let input = this.getFieldValue('NUM');
            let newValue = String(parseInt(input));
            this.setFieldValue(newValue, 'NUM');
        }
    }
};
Blockly.Blocks["local_variable_create"] = {
    init: function () {
        this.jsonInit({
            "message0": "创建局部变量  %1 初始化值 %2",
            "args0": [{
                "type": "field_input",
                "name": "NAME",
            }, {
                "type": "input_value",
                "name": "VALUE",
                "check": ""
            }
            ],
            "style": "struct_blocks",
            "nextStatement": ["inst"],
            "previousStatement": ["inst"]
        });
    },
    onchange(e) {
        if (e.recordUndo) {
            let target = this.getInput('VALUE').connection.targetConnection;
            if (target) {
                let check = target.check_;
                if (check && check.length > 0) {
                    let type = check[0];
                    this.local_variable_info = [{
                        name: this.getFieldValue('NAME'),
                        type: type, blockId: this.id
                    }];
                }
            }
        }
    },
    mutationToDom() {
        if (this.local_variable_info) {
            let xml = '<mutation>' + encodeURI(JSON.stringify(this.local_variable_info)) + '</mutation>';
            let dom = Blockly.Xml.textToDom(xml);
            return dom;
        } else {
            let xml = '<mutation></mutation>';
            let dom = Blockly.Xml.textToDom(xml);
            return dom;
        }
    },
    domToMutation(xml) {
        if (xml && xml.textContent) {
            try {
                let info = JSON.parse(decodeURI(xml.textContent));
                this.local_variable_info = info;
            } catch (e) { }
        }
    }
};
Blockly.Blocks['local_variable_get'] = {
    init() {
        this.jsonInit({
            "type": "local_variable_get",
            "message0": "局部变量 %1",
            "style": "variable_blocks",
            "args0": [{
                "type": "dropdown_local_variables",
                "name": "VAR"
            }],
            "output": null
        });
    },
    onchange(e) {
        if (e.recordUndo && e.blockId === this.id || (e instanceof Blockly.Events.Create)) {
            let f = this.getField('VAR');
            OpenBlock.FieldDropdownLocalVariables.update(f);
            if (f.local_variable_info) {
                this.setOutput(true, f.local_variable_info.type);
            } else {
                this.setOutput(true, null);
            }
        }
    }
};

Blockly.Blocks['local_variable_set'] = {
    init() {
        this.jsonInit({
            "type": "local_variable_set",
            "message0": "设置局部变量 %1 为 %2",
            "style": "variable_blocks",
            "args0": [{
                "type": "dropdown_local_variables",
                "name": "VAR"
            }, {
                "type": 'input_value',
                "name": "VALUE",
                "check": null
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
        });
    },
    onchange(e) {
        if (e.recordUndo && e.blockId === this.id || (e instanceof Blockly.Events.Create)) {
            let f = this.getField('VAR');
            OpenBlock.FieldDropdownLocalVariables.update(f);
            if (f.local_variable_info) {
                this.getInput('VALUE').setCheck(f.local_variable_info.type);
            }
        }
    }
};

Blockly.Extensions.tooltip_when_inline_ = function () {
    this.setTooltip(function () {
        return this.getFieldValue('TEXT');
    }.bind(this));
};
Blockly.Extensions.register('tooltip_when_inline',
    Blockly.Extensions.tooltip_when_inline_);

OpenBlock.FieldDropdownLocalVariables = function (options) {
    OpenBlock.FieldDropdownLocalVariables.superClass_
        .constructor.call(this,
            OpenBlock.FieldDropdownLocalVariables.menuGenerator,
            OpenBlock.FieldDropdownLocalVariables.validator, options);
};
OpenBlock.FieldDropdownLocalVariables.menuGenerator = function () {
    let arr = OpenBlock.FieldDropdownLocalVariables.getAvaliableVar.call(this);
    if (arr.length > 0) {
        return arr.map(v => {
            return [v.name, v.name];
        });
    } else {
        return [["", ""]];
    }
};
OpenBlock.FieldDropdownLocalVariables.validator = function (value) {
    let blk = this.getSourceBlock();
    if (blk) {
        let p;
        if (value && value.length > 0) {
            let arr = OpenBlock.FieldDropdownLocalVariables.getAvaliableVar.call(this);
            p = arr.find(v => {
                return v.name === value;
            });
        }
        if (!p) {
            blk.setWarningText('找不到变量 ' + value);
            this.local_variable_info = null;
        } else {
            this.local_variable_info = p;
            blk.setWarningText();
        }
    }
    return value;
};
OpenBlock.FieldDropdownLocalVariables.getAvaliableVar = function () {
    let blk = this.getSourceBlock();
    if (!blk) {
        return [];
    }
    let env = (blk.workspace._openblock_env || blk.workspace.targetWorkspace._openblock_env);
    if (env && env._openblock_src) {
        let ret = OpenBlock.Blocks.getAvaliableVar(blk.getRootBlock());
        return ret;
    }
    return [];
};
OpenBlock.FieldDropdownLocalVariables.prototype.toXml = function (fieldElement) {
    fieldElement.textContent = this.getValue();
    return fieldElement;
};
OpenBlock.FieldDropdownLocalVariables.prototype.fromXml = function (fieldElement) {
    this.setValue(fieldElement.textContent);
};
OpenBlock.FieldDropdownLocalVariables.update = function (f) {
    f.setValue(f.getValue());
};
OpenBlock.FieldDropdownLocalVariables.fromJson = function (options) {
    return new OpenBlock.FieldDropdownLocalVariables(options);
};
Blockly.utils.object.inherits(OpenBlock.FieldDropdownLocalVariables, Blockly.FieldDropdown);
Blockly.fieldRegistry.register('dropdown_local_variables', OpenBlock.FieldDropdownLocalVariables);
// 事件基础块 +++++++++++++++++
Blockly.Extensions.registerMutator('OpenBlock.Blocks.EventBaseMutator', {
    domToMutation: function (xmlDom) {
        var mutation = document.createElement('mutation');
        if (xmlDom.hasAttribute('eventname')) {
            let evtname = xmlDom.getAttribute('eventname');
            mutation.setAttribute('eventname', evtname);
            this.ub_event_name = evtname;
            let txt = Blockly.Msg[evtname] || evtname;
            this.appendDummyInput().appendField(txt);
            this.setTooltip(txt);
        }
        if (xmlDom.hasAttribute('style')) {
            let style = xmlDom.getAttribute('style');
            mutation.setAttribute('style', style);
            this.setStyle(style);
        }
        if (xmlDom.hasAttribute('colour')) {
            let colour = xmlDom.getAttribute('colour');
            mutation.setAttribute('colour', colour);
        }
        if (xmlDom.hasAttribute('argtype')) {
            let argtype = xmlDom.getAttribute('argtype');
            mutation.setAttribute('argtype', argtype);

            this.appendDummyInput().appendField(Blockly.Msg[argtype] || argtype);
        }
        this.setNextStatement(true, 'inst');
        // this.appendStatementInput('body')
        this.mutationDom = mutation;
    },
    mutationToDom: function () {
        return this.mutationDom;
    }
}, function () { });
Blockly.Blocks["on_event"] = {
    init: function () {
        Blockly.Extensions.apply('OpenBlock.Blocks.EventBaseMutator', this, true);
    }
};
OpenBlock.Blocks.EventBaseListener = function (event) {
    if (event.type === Blockly.Events.BLOCK_MOVE ||
        event.type === Blockly.Events.BLOCK_CREATE ||
        event.type === Blockly.Events.BLOCK_CHANGE) {
        let blks = {}
        let blkArr = Blockly.Workspace.getById(event.workspaceId).getTopBlocks()
        blkArr.forEach(blk => {
            if (blk.isInsertionMarker() || blk.isInFlyout || blk.type !== 'on_event') {
                return;
            }
            if (!blks[blk.ub_event_name]) {
                blks[blk.ub_event_name] = [];
            }
            blks[blk.ub_event_name].push(blk);
        });
        for (let k in blks) {
            let a = blks[k]
            if (a.length > 1) {
                a.forEach(b => {
                    b.setWarningText(Blockly.Msg['DuplicatedEvent'], 'DuplicatedEvent');
                    b.setEnabled(false);
                })
            } else {
                a.forEach(b => {
                    b.setWarningText(null, 'DuplicatedEvent');
                    b.setEnabled(true);
                })
            }
        }
    }
};

OpenBlock.wsBuildCbs.push(s => {
    s.addChangeListener(OpenBlock.Blocks.EventBaseListener);
});

// 事件基础块 ----------------------

Blockly.Blocks["fsm_send_message"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_send_message",
            "message0": "发送消息 %1 给 %2 附加数据 %3",
            "args0": [{
                "type": "field_input",
                "name": "TITLE",
                "check": "String"
            }, {
                "type": "input_value",
                "name": "FSM",
                "check": "FSM"
            }, {
                "type": "input_value",
                "name": "ARG"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["fsm_send_message_after_millisecond"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_send_message_after_millisecond",
            "message0": "等待 %4 毫秒后 %5 发送消息 %1 给 %2 附加数据 %3",
            "args0": [{
                "type": "field_input",
                "name": "TITLE",
                "check": "String"
            }, {
                "type": "input_value",
                "name": "FSM",
                "check": "FSM"
            }, {
                "type": "input_value",
                "name": "ARG"
            }, {
                "type": "input_value",
                "name": "WAIT_MILLISECOND",
                "check": "Integer"
            },
            {
                "type": "input_dummy"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["fsm_broadcast_message"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_broadcast_message",
            "message0": "广播消息 %1 附加数据 %2",
            "args0": [{
                "type": "field_input",
                "name": "TITLE",
                "check": "String"
            }, {
                "type": "input_value",
                "name": "ARG"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["fsm_broadcast_message_after_millisecond"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_broadcast_message_after_millisecond",
            "message0": "等待 %3 毫秒后 %4 广播消息 %1 附加数据 %2",
            "args0": [{
                "type": "field_input",
                "name": "TITLE",
                "check": "String"
            }, {
                "type": "input_value",
                "name": "ARG"
            }, {
                "type": "input_value",
                "name": "WAIT_MILLISECOND",
                "check": "Integer"
            },
            {
                "type": "input_dummy"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["on_message"] = {
    init: function () {
        this.jsonInit({
            "type": "on_message",
            "message0": Blockly.Msg["on_message"],
            "args0": [{
                "type": "field_input",
                "name": "VALUE"
            }],
            "nextStatement": "inst",
            "style": "event_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

    }
};
Blockly.Blocks["on_message_primary"] = {
    init: function () {
        this.jsonInit({
            "type": "on_message_primary",
            "message0": Blockly.Msg["on_message_primary"],
            "args0": [{
                "type": "field_input",
                "name": "VALUE"
            }, {
                "type": "field_dropdown_base_type",
                "name": "TYPE",
            }],
            "nextStatement": "inst",
            "style": "event_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

    }
};
Blockly.Blocks["on_message_struct"] = {
    init: function () {
        this.jsonInit({
            "type": "on_message_struct",
            "message0": Blockly.Msg["on_message_struct"],
            "args0": [{
                "type": "field_input",
                "name": "VALUE"
            }, {
                "type": "field_dropdown_structs",
                "name": "TYPE",
            }],
            "nextStatement": "inst",
            "style": "event_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

    }
};
Blockly.Blocks["recived_message_arg"] = {
    init: function () {
        this.jsonInit({
            "type": "recived_message_arg",
            "message0": "附加数据 %1",
            "args0": [{
                "type": 'field_label_serializable',
                "name": 'TYPE'
            }],
            "output": null
        });
    },
    onchange: function (e) {
        let blk = this.getParent();
        while (blk != null) {
            let type;
            if (blk.type.startsWith('on_message')) {
                type = blk.getFieldValue('TYPE');
            } else if (blk.type.startsWith('on_event')) {
                type = blk.mutationDom.getAttribute('argType');
            } else {
                blk = blk.getParent();
                continue;
            }
            if (type) {
                this.setOutput(true, type);
                this.setFieldValue(type, 'TYPE')
            } else {
                try {
                    this.setOutput(false);
                } catch (e) {
                    // 这里捕获的是由于用户连续操作导致已经创建连接的块马上改变输出属性
                }
                this.setFieldValue('', 'TYPE')
            }
            this.setWarningText();
            return;
        }
        this.setOutput(true);
        this.setWarningText("只可在接收消息或事件时候使用");
    }
}

Blockly.Blocks["recived_message_sender"] = {
    init: function () {
        this.jsonInit({
            "type": "recived_message_sender",
            "message0": "发送者",
            "output": "FSM",
            "style": "event_blocks"
        });
    },
    onchange: function (e) {
        let blk = this.getParent();
        while (blk != null) {
            if (blk.type.startsWith('on_message')) {
            } else {
                blk = blk.getParent();
                continue;
            }
            this.setWarningText();
            return;
        }
        this.setWarningText("只可在接收消时候使用");
    }
}
// 状态切换块 +++++++++++++++

Blockly.Blocks["change_state"] = {
    init: function () {
        this.jsonInit({
            "type": "change_state",
            "message0": Blockly.Msg["change_state"],
            "args0": [{
                "type": "field_dropdown_state",
                "name": "VALUE"
            }],
            "previousStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

    }
};
OpenBlock.FieldDropdownState = function (opt_config) {
    OpenBlock.FieldDropdownState.superClass_.constructor.call(this, OpenBlock.FieldDropdownState.menuGenerator);
}
Blockly.utils.object.inherits(OpenBlock.FieldDropdownState, Blockly.FieldDropdown);
OpenBlock.FieldDropdownState.menuGenerator = function () {
    let blk = this.getSourceBlock();
    if (blk) {
        let workspace = blk.workspace;
        let env = workspace._openblock_env || (workspace.targetWorkspace && workspace.targetWorkspace._openblock_env);
        if (env) {
            let cur_fsm = env._openblock_fsm;
            if (cur_fsm) {
                let opt = [];
                let i = 0;
                cur_fsm.states.forEach(state => {
                    opt.push([state.name, '' + (state.name)]);
                });
                return opt;
            } else {
                return [['', '']];
            }
        }
    }
    return [['', '']];
};
OpenBlock.FieldDropdownState.doClassValidation_ = function (a) {
    return a;
}
OpenBlock.FieldDropdownState.prototype.toXml = function (fieldElement) {
    fieldElement.textContent = this.getValue();
    return fieldElement;
};
OpenBlock.FieldDropdownState.prototype.fromXml = function (fieldElement) {
    this.setValue(fieldElement.textContent);
};
OpenBlock.FieldDropdownState.prototype.setValue = function (newValue) {
    Blockly.Field.prototype.setValue.call(this, newValue);
}
OpenBlock.FieldDropdownState.prototype.getOptions = function (opt_useCache) {
    return OpenBlock.FieldDropdownState.menuGenerator.call(this);
}
OpenBlock.FieldDropdownState.fromJson = function (options) {
    return new OpenBlock.FieldDropdownState(options);
};

Blockly.fieldRegistry.register('field_dropdown_state', OpenBlock.FieldDropdownState);

// 状态切换块 ------------------
// 状态变量 +++++

// OpenBlock.Blocks.state_variable_flyout = function (workspace) {
//     if (!workspace._openblock_env) {
//         return [];
//     }
//     var xmlList = [];
//     // 添加函数

//     var button = document.createElement('button');
//     button.setAttribute('text', '%{BKY_NEW_VARIABLE}');
//     button.setAttribute('callbackKey', 'CREATE_STATE_VARIABLE');


//     xmlList.push(button);

//     var block_s = Blockly.utils.xml.createElement('block');
//     block_s.setAttribute('type', 'state_variables_set');
//     xmlList.push(block_s);
//     var block_g = Blockly.utils.xml.createElement('block');
//     block_g.setAttribute('type', 'state_variables_get');
//     xmlList.push(block_g);

//     return xmlList;
// };
// OpenBlock.Blocks.build_state_variable_flyout = function (workspace) {
//     workspace.registerToolboxCategoryCallback('STATE_VARIABLE_DYNAMIC', OpenBlock.Blocks.state_variable_flyout);
// };

Blockly.Blocks["state_variables_get"] = {
    init: function () {
        this.jsonInit({
            "type": "state_variables_get",
            "message0": Blockly.Msg["state_variables_get"],
            "args0": [{
                "type": "field_dropdown_state_variables",
                "name": "VAR"
            },
            {
                "type": "field_label",
                "name": "TYPE"
            }],
            "output": null,
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

        this.onchange = function (e) {
            if (e.recordUndo) {
                let vid = this.getFieldValue('VAR');
                let env = this.workspace._openblock_env;
                if (env && env._openblock_state) {
                    let variable = env._openblock_state.variables.find(e => e.name === vid);
                    if (variable) {
                        this.outputConnection.setCheck(variable.type);
                        // this.getInput('VALUE').setCheck(variable.type);
                    }
                }
            }
        }
    },
    customContextMenu(options) {
        if (this.isInFlyout) {
            let self = this;
            let workspace = self.workspace.targetWorkspace;
            let o = {
                callback() {
                    let vid = self.getFieldValue('VAR');
                    let env = workspace._openblock_env;
                    if (env && env._openblock_state) {
                        OpenBlock.removeStateVariableByName(env._openblock_state, vid);
                        workspace.getToolbox().clearSelection();
                    }
                },
                enabled: true,
                text: "删除变量"
            };
            options.push(o);
        }
    }
};
Blockly.Blocks["state_variables_set"] = {
    init: function () {
        this.jsonInit({
            "type": "state_variables_set",
            "message0": Blockly.Msg["state_variables_set"],
            "args0": [{
                "type": "field_dropdown_state_variables",
                "name": "VAR"
            }, {
                "type": "input_value",
                "name": "VALUE"
            },
            {
                "type": "field_label",
                "name": "TYPE"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

        this.onchange = function (e) {
            if (e.recordUndo) {
                let vid = this.getFieldValue('VAR');
                let env = this.workspace._openblock_env;
                if (env && env._openblock_state) {
                    let variable = env._openblock_state.variables.find(e => e.name === vid);
                    if (variable) {
                        // this.outputConnection.setCheck(variable.type);
                        this.getInput('VALUE').setCheck(variable.type);
                    }
                }
            }
        }
    },
    customContextMenu(options) {
        if (this.isInFlyout) {
            let self = this;
            let workspace = self.workspace.targetWorkspace;
            let o = {
                callback() {
                    let vid = self.getFieldValue('VAR');
                    let env = workspace._openblock_env;
                    if (env && env._openblock_state) {
                        OpenBlock.removeStateVariableByName(env._openblock_state, vid);
                        workspace.getToolbox().clearSelection();
                    }
                },
                enabled: true,
                text: "删除变量"
            };
            options.push(o);
        }
    }
};
OpenBlock.Blocks.state_variable_flyout = function (workspace) {
    if (!(workspace._openblock_env && workspace._openblock_env._openblock_state)) {
        return [];
    }
    var xmlList = [];
    // 添加函数

    var button = document.createElement('button');
    button.setAttribute('text', '%{BKY_NEW_VARIABLE}');
    button.setAttribute('callbackKey', 'CREATE_STATE_VARIABLE');


    xmlList.push(button);


    let state = workspace._openblock_env._openblock_state;
    state.variables.forEach(v => {
        var block_s = Blockly.utils.xml.createElement('block');
        block_s.setAttribute('type', 'state_variables_set');
        var f = Blockly.utils.xml.createElement('field');
        f.setAttribute('name', 'VAR');
        f.textContent = v.name;
        block_s.appendChild(f);
        xmlList.push(block_s);
        var block_g = Blockly.utils.xml.createElement('block');
        block_g.setAttribute('type', 'state_variables_get');
        f = Blockly.utils.xml.createElement('field');
        f.setAttribute('name', 'VAR');
        f.textContent = v.name;
        block_g.appendChild(f);
        xmlList.push(block_g);
    });
    var block_s = Blockly.utils.xml.createElement('block');
    block_s.setAttribute('type', 'state_variables_set');
    xmlList.push(block_s);
    var block_g = Blockly.utils.xml.createElement('block');
    block_g.setAttribute('type', 'state_variables_get');
    xmlList.push(block_g);

    return xmlList;
};
OpenBlock.Blocks.build_state_variable_flyout = function (workspace) {
    workspace.registerToolboxCategoryCallback('STATE_VARIABLE_DYNAMIC', OpenBlock.Blocks.state_variable_flyout);
};
OpenBlock.wsBuildCbs.push(OpenBlock.Blocks.build_state_variable_flyout);
// 状态变量 -----

OpenBlock.wsBuildCbs.push(function (workspace) {
    workspace.registerButtonCallback('CREATE_STATE_VARIABLE', function (button) {
        workspace.getToolbox().clearSelection();
        if (typeof (OpenBlock.config.uiCallbacks.addStateVariable) === 'function') {
            let workspace = button.getTargetWorkspace();
            OpenBlock.config.uiCallbacks.addStateVariable(workspace._openblock_env._openblock_src, workspace._openblock_env._openblock_state);
        } else {
            Blockly.Variables.createVariableButtonHandler(button.getTargetWorkspace());
        }
    });
});
OpenBlock.FieldDropdownSTATEVariables = function (opt_config) {
    OpenBlock.FieldDropdownSTATEVariables.superClass_.constructor.call(this, OpenBlock.FieldDropdownSTATEVariables.menuGenerator);
}
Blockly.utils.object.inherits(OpenBlock.FieldDropdownSTATEVariables, Blockly.FieldDropdown);
OpenBlock.FieldDropdownSTATEVariables.menuGenerator = function () {
    let blk = this.getSourceBlock();
    if (blk) {
        let workspace = blk.workspace;
        let env = workspace._openblock_env || (workspace.targetWorkspace && workspace.targetWorkspace._openblock_env);
        if (env) {
            let cur_state = env._openblock_state;
            if (cur_state) {
                let opt = [];
                let i = 0;
                cur_state.variables.forEach(state => {
                    opt.push([state.name, state.name]);
                });
                return opt;
            } else {
                return [['', '']];
            }
        }
    }
    return [['', '']];
};
OpenBlock.FieldDropdownSTATEVariables.doClassValidation_ = function (a) {
    return a;
}
OpenBlock.FieldDropdownSTATEVariables.prototype.doValueUpdate_ = function (newValue) {
    OpenBlock.FieldDropdownSTATEVariables.superClass_.doValueUpdate_.call(this, newValue);
    var options = this.getOptions(true);
    for (var i = 0, option; (option = options[i]); i++) {
        if (option[1] === this.value_) {
            this.selectedOption_ = option;
            let blk = this.getSourceBlock();
            if (blk) {
                blk.setWarningText();
                let workspace = blk.workspace;
                let env = workspace._openblock_env || (workspace.targetWorkspace && workspace.targetWorkspace._openblock_env);
                if (env) {
                    let cur_fsm = env._openblock_state;
                    if (cur_fsm) {
                        for (let k in cur_fsm.variables) {
                            let v = cur_fsm.variables[k];
                            if (v.name === newValue) {
                                let typeinput = blk.getField('TYPE');
                                if (typeinput) {
                                    typeinput.setValue(OpenBlock.i(v.type));
                                }
                                if (blk.outputConnection) {
                                    blk.outputConnection.setCheck(v.type)
                                }
                                let valueInput = blk.getInput('VALUE');
                                if (valueInput) {
                                    valueInput.setCheck(v.type);
                                }
                                break;
                            }
                        }
                    }
                }
            }
            return;
        }
    }
    this.selectedOption_ = [newValue, newValue];
    let blk = this.getSourceBlock();
    if (blk) {
        blk.setWarningText('找不到变量 ' + newValue);
    }
};
OpenBlock.FieldDropdownSTATEVariables.prototype.toXml = function (fieldElement) {
    fieldElement.textContent = this.getValue();
    return fieldElement;
};
OpenBlock.FieldDropdownSTATEVariables.prototype.fromXml = function (fieldElement) {
    this.setValue(fieldElement.textContent);
};
OpenBlock.FieldDropdownSTATEVariables.prototype.setValue = function (newValue) {
    Blockly.Field.prototype.setValue.call(this, newValue);
}
OpenBlock.FieldDropdownSTATEVariables.prototype.getOptions = function (opt_useCache) {
    return OpenBlock.FieldDropdownSTATEVariables.menuGenerator.call(this);
}
OpenBlock.FieldDropdownSTATEVariables.fromJson = function (options) {
    return new OpenBlock.FieldDropdownSTATEVariables(options);
};

Blockly.fieldRegistry.register('field_dropdown_state_variables', OpenBlock.FieldDropdownSTATEVariables);

// 状态机变量 ++++++++++++++++++++

Blockly.Blocks["fsm_variables_get"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_variables_get",
            "message0": Blockly.Msg["fsm_variables_get"],
            "args0": [{
                "type": "field_dropdown_fsm_variables",
                "name": "VAR"
            },
            {
                "type": "field_label",
                "name": "TYPE"
            }],
            "output": null,
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

        this.onchange = function (e) {
            if (e.recordUndo) {
                let vid = this.getFieldValue('VAR');
                let env = this.workspace._openblock_env;
                if (env && env._openblock_fsm) {
                    let variable = env._openblock_fsm.variables.find(e => e.name === vid);
                    if (variable) {
                        this.outputConnection.setCheck(variable.type);
                        this.setWarningText();
                    } else {
                        this.setWarningText("变量不存在");
                    }
                }
            }
        }
    },
    customContextMenu(options) {
        if (this.isInFlyout) {
            let self = this;
            let workspace = self.workspace.targetWorkspace;
            let o = {
                callback() {
                    let vid = self.getFieldValue('VAR');
                    let env = workspace._openblock_env;
                    if (env && env._openblock_fsm) {
                        OpenBlock.removeFSMVariableByName(env._openblock_fsm, vid);
                        workspace.getToolbox().clearSelection();
                    }
                },
                enabled: true,
                text: "删除变量"
            };
            options.push(o);
        }
    }
};

Blockly.Blocks["fsm_variables_set"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_variables_set",
            "message0": Blockly.Msg["fsm_variables_set"],
            "args0": [{
                "type": "field_dropdown_fsm_variables",
                "name": "VAR"
            }, {
                "type": "input_value",
                "name": "VALUE"
            },
            {
                "type": "field_label",
                "name": "TYPE"
            }],
            "previousStatement": "inst",
            "nextStatement": "inst",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });

        this.onchange = function (e) {
            if (e.recordUndo) {
                let vid = this.getFieldValue('VAR');
                let env = this.workspace._openblock_env;
                if (env && env._openblock_fsm) {
                    let variable = env._openblock_fsm.variables.find(e => e.name === vid);
                    if (variable) {
                        this.getInput('VALUE').setCheck(variable.type);
                        this.setWarningText();
                    } else {
                        this.setWarningText("变量不存在");
                    }
                }
            }
        }
    },
    customContextMenu(options) {
        if (this.isInFlyout) {
            let self = this;
            let workspace = self.workspace.targetWorkspace;
            let o = {
                callback() {
                    let vid = self.getFieldValue('VAR');
                    let env = workspace._openblock_env;
                    if (env && env._openblock_fsm) {
                        OpenBlock.removeFSMVariableByName(env._openblock_fsm, vid);
                        workspace.getToolbox().clearSelection();
                    }
                },
                enabled: true,
                text: "删除变量"
            };
            options.push(o);
        }
    }
};
OpenBlock.Blocks.fsm_variable_flyout = function (workspace) {
    if (!(workspace._openblock_env && workspace._openblock_env._openblock_fsm)) {
        return [];
    }
    var xmlList = [];
    // 添加函数

    var button = document.createElement('button');
    button.setAttribute('text', '%{BKY_NEW_VARIABLE}');
    button.setAttribute('callbackKey', 'CREATE_FSM_VARIABLE');


    xmlList.push(button);
    let fsm = workspace._openblock_env._openblock_fsm;
    fsm.variables.forEach(v => {
        var block_s = Blockly.utils.xml.createElement('block');
        block_s.setAttribute('type', 'fsm_variables_set');
        var f = Blockly.utils.xml.createElement('field');
        f.setAttribute('name', 'VAR');
        f.textContent = v.name;
        block_s.appendChild(f);
        xmlList.push(block_s);
        var block_g = Blockly.utils.xml.createElement('block');
        block_g.setAttribute('type', 'fsm_variables_get');
        f = Blockly.utils.xml.createElement('field');
        f.setAttribute('name', 'VAR');
        f.textContent = v.name;
        block_g.appendChild(f);
        xmlList.push(block_g);
    });
    var block_s = Blockly.utils.xml.createElement('block');
    block_s.setAttribute('type', 'fsm_variables_set');
    xmlList.push(block_s);
    var block_g = Blockly.utils.xml.createElement('block');
    block_g.setAttribute('type', 'fsm_variables_get');
    xmlList.push(block_g);

    return xmlList;
};
OpenBlock.Blocks.build_fsm_variable_flyout = function (workspace) {
    workspace.registerToolboxCategoryCallback('FSM_VARIABLE_DYNAMIC', OpenBlock.Blocks.fsm_variable_flyout);
};
OpenBlock.wsBuildCbs.push(OpenBlock.Blocks.build_fsm_variable_flyout);

OpenBlock.wsBuildCbs.push(function (workspace) {
    if (workspace.getToolbox()) {
        let t = workspace.getToolbox();
        t.clearSelection();
    }
    workspace.registerButtonCallback('CREATE_FSM_VARIABLE', function (button) {
        let workspace = button.getTargetWorkspace();
        if (workspace.getToolbox()) {
            let t = workspace.getToolbox();
            t.clearSelection();
        }
        if (typeof (OpenBlock.config.uiCallbacks.addFsmVariable) === 'function') {
            OpenBlock.config.uiCallbacks.addFsmVariable(workspace._openblock_env._openblock_src, workspace._openblock_env._openblock_fsm);
        } else {
            Blockly.Variables.createVariableButtonHandler(button.getTargetWorkspace());
        }
    });
});
OpenBlock.FieldDropdownFSMVariables = function (opt_config) {
    OpenBlock.FieldDropdownFSMVariables.superClass_.constructor.call(this, OpenBlock.FieldDropdownFSMVariables.menuGenerator);
}
Blockly.utils.object.inherits(OpenBlock.FieldDropdownFSMVariables, Blockly.FieldDropdown);
OpenBlock.FieldDropdownFSMVariables.menuGenerator = function () {
    let blk = this.getSourceBlock();
    if (blk) {
        let workspace = blk.workspace;
        let env = workspace._openblock_env || (workspace.targetWorkspace && workspace.targetWorkspace._openblock_env);
        if (env) {
            let cur_fsm = env._openblock_fsm;
            if (cur_fsm) {
                let opt = [];
                cur_fsm.variables.forEach(state => {
                    opt.push([state.name, state.name]);
                });
                return opt;
            } else {
                return [['', '']];
            }
        }
    }
    return [['', '']];
};
OpenBlock.FieldDropdownFSMVariables.prototype.doClassValidation_ = function (a) {
    return a;
};

OpenBlock.FieldDropdownFSMVariables.prototype.doValueUpdate_ = function (newValue) {
    OpenBlock.FieldDropdownFSMVariables.superClass_.doValueUpdate_.call(this, newValue);
    var options = this.getOptions(true);
    for (var i = 0, option; (option = options[i]); i++) {
        if (option[1] === this.value_) {
            this.selectedOption_ = option;
            let blk = this.getSourceBlock();
            if (blk) {
                blk.setWarningText();
                let workspace = blk.workspace;
                let env = workspace._openblock_env || (workspace.targetWorkspace && workspace.targetWorkspace._openblock_env);
                if (env) {
                    let cur_fsm = env._openblock_fsm;
                    if (cur_fsm) {
                        for (let k in cur_fsm.variables) {
                            let v = cur_fsm.variables[k];
                            if (v.name === newValue) {
                                let typeinput = blk.getField('TYPE');
                                if (typeinput) {
                                    typeinput.setValue(OpenBlock.i(v.type));
                                }
                                if (blk.outputConnection) {
                                    blk.outputConnection.setCheck(v.type)
                                }
                                let valueInput = blk.getInput('VALUE');
                                if (valueInput) {
                                    valueInput.setCheck(v.type);
                                }
                                break;
                            }
                        }
                    }
                }
            }
            return;
        }
    }
    this.selectedOption_ = [newValue, newValue];
    let blk = this.getSourceBlock();
    if (blk) {
        blk.setWarningText('找不到变量 ' + newValue);
    }
};
OpenBlock.FieldDropdownFSMVariables.prototype.toXml = function (fieldElement) {
    fieldElement.textContent = this.getValue();
    return fieldElement;
};
OpenBlock.FieldDropdownFSMVariables.prototype.fromXml = function (fieldElement) {
    this.setValue(fieldElement.textContent);
};
OpenBlock.FieldDropdownFSMVariables.prototype.setValue = function (newValue) {
    Blockly.Field.prototype.setValue.call(this, newValue);
}
OpenBlock.FieldDropdownFSMVariables.prototype.getOptions = function (opt_useCache) {
    return OpenBlock.FieldDropdownFSMVariables.menuGenerator.call(this);
}
OpenBlock.FieldDropdownFSMVariables.fromJson = function (options) {
    return new OpenBlock.FieldDropdownFSMVariables(options);
};

Blockly.fieldRegistry.register('field_dropdown_fsm_variables', OpenBlock.FieldDropdownFSMVariables);
// 状态机变量 ---------------------

Blockly.Blocks['empty_provider'] = {
    init: function () {
        this.jsonInit({
            "output": null
        });
    },
    mutationToDom() {
        let xml = '<mutation>' + this.mutation + '</mutation>';
        let dom = Blockly.Xml.textToDom(xml);
        return dom;
    },
    domToMutation(xml) {
        if (xml && xml.textContent) {
            this.mutation = xml.textContent;
            this.updateBlock();
        }
    },
    updateBlock() {
        this.setShadow(true);
        let mutationData = JSON.parse(decodeURI(this.mutation), Deserializer);
        let parentType = mutationData.parentType;
        let arg = mutationData.argName;
        let blkprovider = OpenBlock.NativeBlockProviderConfig[parentType];
        if (blkprovider) {
            /**
             * @type {OB_DropdownProvider}
             */
            let provider = blkprovider[arg];
            if (provider) {
                this.setOutput(mutationData.checkType);
                let subinput = this.appendDummyInput();
                subinput.appendField(new Blockly.FieldDropdown(provider.options), 'VALUE');
            } else {
                console.warn('找不到本地块供给 ' + parentType + ":" + arg);
            }
        } else {
            console.warn('找不到本地块供给 ' + parentType);
        }
    }
};
// 状态机控制 ++++++++++++++++++++

Blockly.Blocks['fsm_provider'] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_provider",
            "message0": "%1",
            "args0": [{
                "type": "field_dropdown_fsm_list",
                "name": "FSM"
            }],
            "output": "String",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["fsm_create"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_create",
            "message0": Blockly.Msg["fsm_create"],
            "args0": [{
                "type": "field_dropdown_fsm_list",
                "name": "FSM"
            }],
            "output": "FSM",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["find_fsm_by_name"] = {
    init: function () {
        this.jsonInit({
            "type": "find_fsm_by_name",
            "message0": Blockly.Msg["find_fsm_by_name"],
            "args0": [{
                "type": "input_value",
                "name": "NAME",
                "check": 'String'
            }],
            "output": "FSM",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["find_fsm_by_type"] = {
    init: function () {
        this.jsonInit({
            "type": "find_fsm_by_type",
            "message0": Blockly.Msg["find_fsm_by_type"],
            "args0": [{
                "type": "field_dropdown_fsm_list",
                "name": "FSM"
            }],
            "output": "list<FSM>",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
Blockly.Blocks["fsm_create_unnamed"] = {
    init: function () {
        this.jsonInit({
            "type": "fsm_create_unnamed",
            "message0": Blockly.Msg["fsm_create_unnamed"],
            "output": "String",
            "style": "control_blocks",
            "tooltip": "",
            "helpUrl": ""
        });
    }
};
OpenBlock.FieldDropdownFSMList = function (opt_config) {
    OpenBlock.FieldDropdownFSMList.superClass_.constructor.call(this, OpenBlock.FieldDropdownFSMList.menuGenerator);
}
Blockly.utils.object.inherits(OpenBlock.FieldDropdownFSMList, Blockly.FieldDropdown);
OpenBlock.FieldDropdownFSMList.menuGenerator = function () {
    let list = [];
    OpenBlock.BlocklyParser.loadedFiles.analyzedModules.forEach(m => {
        m.fsms.forEach(fsm => {
            let name = m.name + "." + fsm.name;
            list.push([name, name]);
        });
    });
    if (list.length === 0) {
        list.push(["", ""]);
    }
    return list;
};
OpenBlock.FieldDropdownFSMList.prototype.doClassValidation_ = function (a) {
    return a;
};
/**
 * Update the value of this dropdown field.
 * @param {*} newValue The value to be saved. The default validator guarantees
 * that this is one of the valid dropdown options.
 * @protected
 */
OpenBlock.FieldDropdownFSMList.prototype.doValueUpdate_ = function (newValue) {
    OpenBlock.FieldDropdownFSMList.superClass_.doValueUpdate_.call(this, newValue);
    var options = this.getOptions(true);
    for (var i = 0, option; (option = options[i]); i++) {
        if (option[1] === this.value_) {
            this.selectedOption_ = option;
            let blk = this.getSourceBlock();
            if (blk) {
                blk.setWarningText();
            }
            return;
        }
    }
    this.selectedOption_ = [newValue, newValue];
    let blk = this.getSourceBlock();
    if (blk) {
        blk.setWarningText('找不到状态机 ' + newValue);
    }
};
OpenBlock.FieldDropdownFSMList.prototype.toXml = function (fieldElement) {
    fieldElement.textContent = this.getValue();
    return fieldElement;
};
OpenBlock.FieldDropdownFSMList.prototype.fromXml = function (fieldElement) {
    this.setValue(fieldElement.textContent);
};
OpenBlock.FieldDropdownFSMList.prototype.setValue = function (newValue) {
    Blockly.Field.prototype.setValue.call(this, newValue);
}
OpenBlock.FieldDropdownFSMList.prototype.getOptions = function (opt_useCache) {
    return OpenBlock.FieldDropdownFSMList.menuGenerator.call(this);
}
OpenBlock.FieldDropdownFSMList.fromJson = function (options) {
    return new OpenBlock.FieldDropdownFSMList(options);
};

Blockly.fieldRegistry.register('field_dropdown_fsm_list', OpenBlock.FieldDropdownFSMList);
// 状态机控制 ---------------------

// Blockly.Blocks["sort"] // TODO

Blockly.Blocks['native_call'] = {
    init() {
        this.setStyle("native_call");
    },
    mutationToDom() {
        let xml = '<mutation>' + this.mutation + '</mutation>';
        let dom = Blockly.Xml.textToDom(xml);
        return dom;
    },
    domToMutation(xml) {
        if (xml && xml.textContent) {
            this.mutation = xml.textContent;
            this.mutationData = JSON.parse(decodeURI(this.mutation), Deserializer);
            this.appendDummyInput().appendField(OpenBlock.i(this.mutationData.func.fullname)
                + (this.mutationData.func.returnType ? (":" + OpenBlock.i(this.mutationData.func.returnType.toCodeText())) : ""));
            this.updateBlock();
        }
    },
    getConfig(fieldName) {
        let b = OpenBlock.NativeBlockProviderConfig[this.mutationData.func.fullname];
        if (b) {
            return b[fieldName];
        }
    },
    updateBlock() {
        let func = OpenBlock.nativefunctions.check(this.mutationData.func, false);
        if (func) {
            this.mutationData.func = func;
            this.setWarningText();
        } else {
            func = this.mutationData.func;
            this.setWarningText("函数库已经发生改变，请用其他块替代");
        }
        let block = this;
        Blockly.Events.disable();
        if (func.returnType && !this.mutationData.ignoreReturnValue) {
            if (block.nextConnection && block.nextConnection.isConnected()) {
                block.nextConnection.disconnect();
            }
            block.setNextStatement(false);
            if (block.previousConnection && block.previousConnection.isConnected()) {
                block.previousConnection.disconnect();
            }
            block.setPreviousStatement(false);
            block.setOutput(true, func.returnType.toCodeText());
        } else {
            if (block.outputConnection && block.outputConnection.isConnected()) {
                block.outputConnection.disconnect();
            }
            block.setOutput(false);
            block.setNextStatement(true, 'inst');
            block.setPreviousStatement(true, 'inst');
        }
        for (let i = 0; i < func.args.length; i++) {
            let input = block.inputList[1 + i];
            let arg = func.args[i];
            let type;
            if (arg.type) {
                if (!arg.type.toCodeText) {
                    debugger
                }
                type = arg.type.toCodeText();
            }
            if (input) {
                input.fieldRow[0].setValue(OpenBlock.i(arg.name) + ":" + OpenBlock.i(type));
                input.setCheck(type);
            }
            else {
                let name = arg.name;
                let input = block.appendValueInput(name);
                input.setAlign(Blockly.ALIGN_RIGHT).setCheck(type);
                input.appendField(OpenBlock.i(arg.name) + ":" + OpenBlock.i(type));
                let conf = this.getConfig(name);
                if (conf) {
                    conf.makeShadowBlock(this, type, name, input);
                }
            }
        }
        while (block.inputList.length > func.args.length + 1) {
            let input = block.inputList[block.inputList.length - 1];
            if (input.connection && input.connection.isConnected()) {
                input.connection.disconnect();
            }
            block.removeInput(input.name);
        }
        Blockly.Events.enable();
    }
};